透過減少 ttf 字體檔案大小來增進網頁效能


Posted by ArvinH on 2021-04-25

前言

最近公司有個行銷活動需要做一個簡單的 SPA,基本上只有簡單的三個頁面,完全可以利用 Gatsby 或 Nextjs 來製作靜態頁面,然後部署到 CDN 上頭,效能上來說理應足夠好了,但是我們的設計師在頁面上混用了多種的字體,尤其是日文部分,除了一般瀏覽器內建的字體外,某些元件採用額外的免費字體,例如 Corporate Logo Font,這代表我們需要額外去下載這些字型,但為了頁面上的幾個字,去下載一整個字型檔案(ttf, 2.6MB)實在很浪費,因此只好來研究一下如何客製化字型檔案,只載入我們需要的字。

雖然這感覺是個很容易遇到的需求,但我還真的是第一次實際需要處理,感謝同事 Carlos 提供解法,透過這篇文章筆記一下,希望對他人也有點幫助。

開始前先稍稍複習一下,什麼是 ttf?還有什麼其他字型格式呢?

TTF(TrueType Font)是由蘋果和微軟共同開發的一種電腦輪廓字型類型標準,是 Mac 與 Windows 上最常見的格式,基本上所有主流瀏覽器都支援,也是免費或便宜的第三方字體最常提供的格式。缺點是檔案未經過壓縮,文件大小較大。

另一個主流格式為 OTF(OpenType Font),是一種可縮放字型(scalable font)電腦字型類型,由 TrueType 延伸而來,採用 PostScript 格式,是微軟與 Adobe 聯合開發,用來替代 TrueType 字型的新字型。

WOFF(Web Open Font Format) 則是完全為了 Web 而設計的格式,由 Mozilla、Microsoft 與 Opera 合作推出。WOFF 的字型都經由 WOFF 的編碼工具壓縮,體積能比 tff 小 40%,現在已經是網頁字體的推薦標準。WOFF2 則是 WOFF 的升級版,體積可以壓得更小。

最後,當然大家熟悉的 SVG 也可以算是一種。

在主流的作業系統與瀏覽器上,這幾種格式的支援度都很高,而其主要的細節差異,因為非本篇重點,就不多著墨,有興趣的讀者可以到 wiki 上查看。

今天要減少的字型檔案為最常見的 tff 格式。

順帶一提,當我在撰寫文章的時候,對於字型、字體等名詞的差異很模糊,好在 JustFont 在多年前的一篇文章解釋得蠻清楚的,推薦大家理解一下!

Setup

要針對字型進行處理的話,首先我們需要下載 FontForge,FontForge 是一個很有名的軟體,可以用來設計、創建字體,或是進行各種字型相關的操作,可以從 https://fontforge.org/en-US/downloads/mac-dl/ 下載 Mac 版本(也有 Linux 與 Windows 的版本)。

在官網上你可以找到許多文件,甚至是一整個 ebook 來教你如何用 FontForge 來設計字體。

用 FontForge 開啟原始字型檔

載好 FontForge 後,我們先打開原始的字型檔,這邊以前面提到的 Corporate Logo Font 為例(註: 在 MacOS Catalina 或 Big Sur,直接點選載好在 Apps 內的 FontForge app 可能會被 OS 擋下來,快一點的方式是按右鍵 -> "Show Package Contents" -> "Contents" -> "MacOS",然後點選 FontForg.app,接者就會打開 terminal 並執行 FontForge。):

original-font-file-in-fontforge

開啟後可以看到所有的字圖,接著其實你就可以選取你不要的字圖,然後 clear 掉它們:

clear-font

但照這樣處理,弄到天荒地老六親不認都弄不完。工程師要用更聰明的解法。

正確的姿勢

打從一開始,我們就是因為原始字檔裡面太多我們不要的東西,我們需要的很少,才想要從原始檔案中擷取需要的部分,既然如此,就應該從我們想要的字圖下手,而不是慢慢刪掉我們不要的字圖。

FontForge 其實有提供一個很方便的功能,叫做 Invert Selection,能夠選取所有你沒有選取到的東西,直接看個動圖範例:

fontforge-invert-select

這樣一來,就很簡單了,只要選取住你想要的字圖,然後點選 Edit -> Select -> Invert Selection,就完成了,接著就把 Fontforge 自動幫你選取的字圖 clear 掉即可。

但這樣還是有個問題。

字型檔內容這麼多,我要手動在 FontForge 中找到自己想要的字圖不也是找到山窮水盡嗎?

正確姿勢二

FontForge 是個蠻強大的工具,除了 GUI 以外,也提供 interpreters,讓你能撰寫 scripts 來修改字型檔。

一個 interpreter 是 Python,另一個則是其內建的 scripting language。詳細的範例、語法等可以從官網查看,文件很完整

有了 scripting 的功能,我們就不用自己手動選取字圖啦。

在 FontForge UI 上,你可以點選 File -> Execute Script 叫出 Dialog,可以選擇直接貼上 Python 程式碼,也可以選擇 FF -> Call,來載入使用另一個內建 interpreter 的 script file。

fontforge-exec-scripts

因為我們要處理的動作很簡單,只有三個動作(選取字型、反轉選取、刪除),所以直接用內建的 script language 其實比較簡單,可以利用 NodeJS 來產生執行檔。

需要的 API

三個動作,選取字圖、反轉選取、刪除,分別對應的 API 為 SelectMore()SelectInvert()DetachAndRemoveGlyphs()

我們要匯入進 FontForge 的執行檔,就只需要這三個 API 即可。

SelectMore() 用法是傳入字型的 unicode 作為參數,即可選取該字圖,不過執行一次只能選取一個字圖。SelectInvert()DetachAndRemoveGlyphs() 則不需要參數。

範例程式

知道了需要的 API,我們就可以來寫程式產生執行檔 subset-font.pe.pe 是 FontForge 可以接受的格式,.ff 也行:

// 挑選出你頁面上需要用到的字
const characters = '123招待コード';

const stream = fs.createWriteStream('./subset-font.pe');

stream.once('open', function (fd) {
  characters.split('').forEach(char => {
    // 轉換成 16 進位
    let hex = char.charCodeAt(0).toString(16);
    // 補零,以符合 \u 格式
    if (hex.length < 4) {
      hex = hex.padStart(4, '0');
    }
    // 然後執行檔內寫入 SelectMore
    stream.write(`SelectMore("u${hex}")\n`);
  });
  // 反轉選擇,選取所有其他不要的字
  stream.write('SelectInvert()\n');
  // 最後移除字型
  stream.write('DetachAndRemoveGlyphs()\n');
  stream.end();
});

利用 FontForge API 搭配上面程式後,會產生以下內容:

sh subset-font.pe SelectMore("u0031") SelectMore("u0032") SelectMore("u0033") SelectMore("u62db") SelectMore("u5f85") SelectMore("u30b3") SelectMore("u30fc") SelectMore("u30c9") SelectInvert() DetachAndRemoveGlyphs()

接著依照正確姿勢二的方式,匯入此執行檔,FontForge 就會產生只包含我們想要的字圖的字型檔了!:

fontforge-script-subset-font

不過因為刪掉的字圖很多,我們可以進一步透過 FontForge 的壓縮功能來輔助我們檢視成品:

剛執行完 script 後,畫面會停留在 select 所有其他你不要的字圖的狀態,你可以先隨便點選空白處 deselect 所有字圖,然後選擇 Encoding -> Compact

fontforge-compact

就能清楚看到整個檔案的確只剩下我們所選的字圖(以上面範例 script 來說就是 123招待コード)。

最後步驟就是產生字型檔案,點選 File -> Generate Fonts...,然後看你要 export 成什麼格式,如果是網頁上要用,當然就推薦使用 woff

fontforge-subset-font-generate-file

按下 Generate 後可能會出現 Error,可以不用理他,繼續 generate:

fontforge-generate-error

這樣就大功告成了!

(註:關掉 FontForge 時,記得選 Don't Save,不然會蓋掉原始的檔案喔!)

結論

這個方式可以用在各種字體檔案,非常方便,對於靜態頁面上內容文字不太會變動的狀況下,利用這個技巧可以大幅降低需要載入的檔案大小,以我公司專案的例子來說,從原本 2.6MB 的 tff 檔案,最後可以變成 8KB 的 woff 檔案,省下的大小很可觀的。

簡單的筆記,希望對大家有幫助!

資料來源

  1. Web 字體簡介: TTF, OTF, WOFF, EOT & SVG
  2. Delete all unused characters from a TTF-font with Fontforge
  3. fontforge docs
  4. [but] 雜談 ─ 常常搞混的一些詞

#Web #font #performance









Related Posts

Jest "Cannot find module from xxx" issue

Jest "Cannot find module from xxx" issue

Fake Vapes and How to Avoid Them

Fake Vapes and How to Avoid Them

【Day00】前言

【Day00】前言




Newsletter




Comments